/*
 * Copyright (c) 2014, the Dart project authors.  Please see the AUTHORS file
 * for details. All rights reserved. Use of this source code is governed by a
 * BSD-style license that can be found in the LICENSE file.
 */
/** 
 * @description This test ensures that putImageData works correctly, the end
 * result should be a 100x100px green square.
 */
import "dart:html";
import "../../testcommon.dart";
import "../../../Utils/async_utils.dart";

var NaN = double.NAN;
var Inf = double.INFINITY;
var NegInf = double.NEGATIVE_INFINITY;

main() {
  document.body.setInnerHtml('''
      <canvas id="canvas" width="100" height="100"></canvas><br/>
      ''', treeSanitizer: new NullTreeSanitizer());

  var context;

  fillRect(imageData, x, y, width, height, r, g, b, a)
  {
    var bytesPerRow = imageData.width * 4;
    var data =imageData.data;
    for (var i = 0; i < height; i++) {
      var rowOrigin = (y+i) * bytesPerRow;
      rowOrigin += x * 4;
      for (var j = 0; j < width; j++) {
        var position = rowOrigin + j * 4;
        data[position + 0] = r;
        data[position + 1] = g;
        data[position + 2] = b;
        data[position + 3] = a;
      }
    }
  }

  dataToArray(data) {
    var result = new List(data.length);
    for (var i = 0; i < data.length; i++)
      result[i] = data[i];
    return result;
  }

  getPixel(x, y) {
    var data = context.getImageData(x,y,1,1);
    if (data == null) // getImageData failed, which should never happen
      return [-1,-1,-1,-1];
    return dataToArray(data.data);
  }

  pixelShouldBe(x, y, colour) {
    shouldBeList(getPixel(x, y), colour);
  }

  var canvas = document.getElementById("canvas");
  context = canvas.getContext("2d");

  var buffer = context.createImageData(100,100);
  // Fill top left corner
  fillRect(buffer, 0, 0, 50, 50, 0, 128,0,255);
  context.putImageData(buffer, 0, 0);
  pixelShouldBe( 0,  0, [0, 128,0,255]);
  pixelShouldBe(25, 25, [0, 128,0,255]);
  pixelShouldBe(49,  0, [0, 128,0,255]);
  pixelShouldBe( 0, 49, [0, 128,0,255]);
  pixelShouldBe(49, 49, [0, 128,0,255]);
  pixelShouldBe(50,  0, [0, 0, 0, 0]);
  pixelShouldBe( 0, 50, [0, 0, 0, 0]);
  pixelShouldBe(50, 50, [0, 0, 0, 0]);

  // Test positioned drawing -- make bottom right green
  context.putImageData(buffer, 0, 50);
  pixelShouldBe( 0, 50, [0, 128,0,255]);
  pixelShouldBe(25, 75, [0, 128,0,255]);
  pixelShouldBe(49, 50, [0, 128,0,255]);
  pixelShouldBe( 0, 99, [0, 128,0,255]);
  pixelShouldBe(49, 99, [0, 128,0,255]);

  // Test translation doesn't effect putImageData
  context.translate(50, -50);
  context.putImageData(buffer, 50, 50);
  pixelShouldBe(50, 50, [0, 128,0,255]);
  pixelShouldBe(75, 75, [0, 128,0,255]);
  pixelShouldBe(99, 99, [0, 128,0,255]);
  pixelShouldBe(50, 49, [0, 0, 0, 0]);
  context.translate(-50, 50);

  // Test dirty rect handling
  buffer = context.createImageData(50,50);
  fillRect(buffer, 0, 0, 50, 50, 0, 128, 0, 255);
  context.putImageData(buffer, 50, 0);
  fillRect(buffer, 0, 0, 50, 50, 255, 0, 0, 255);
  context.putImageData(buffer, 50, 0, 15, 15, 20, 20);
  context.fillStyle="rgb(0,128,0)";
  context.fillRect(65, 15, 20, 20);
  var points = [0, 5, 15, 25, 35, 45];
  for (var x = 0; x < points.length; x++)
    for (var y = 0; y < points.length; y++)
      pixelShouldBe(points[x] + 50, points[y], [0, 128, 0, 255]);

  // Test drawing outside the canvas border
  fillRect(buffer, 0, 0, 50, 50, 255, 0, 0, 255);
  context.putImageData(buffer, -50, 0);
  pixelShouldBe(0, 25, [0, 128,0,255]);
  context.putImageData(buffer, 100, 0);
  pixelShouldBe(99, 25, [0, 128,0,255]);
  context.putImageData(buffer, 0, -50);
  pixelShouldBe(25, 0, [0, 128,0,255]);
  context.putImageData(buffer, 0, 100);
  pixelShouldBe(25, 99, [0, 128,0,255]);

  // test drawing with non-intersecting dirty rect
  context.putImageData(buffer, 50, 0,  50, 0, 100, 100);
  context.putImageData(buffer, 50, 0, -50, 0, 50, 100);
  context.putImageData(buffer, 50, 0,  0, 50, 100, 100);
  context.putImageData(buffer, 50, 0,  50, -50, 100, 100);
  for (var x = 0; x < points.length; x++)
    for (var y = 0; y < points.length; y++)
      pixelShouldBe(points[x] + 50, points[y], [0, 128, 0, 255]);

  // Test drawing to region intersect edge of canvas
  buffer = context.createImageData(100, 100);
  fillRect(buffer, 0, 0, 100, 100, 0, 128, 0, 255);
  fillRect(buffer, 10, 10, 80, 80, 255, 0, 0, 255);

  //left edge
  context.putImageData(buffer, -90, 0);
  pixelShouldBe(0, 25, [0, 128,0,255]);
  pixelShouldBe(0, 50, [0, 128,0,255]);
  pixelShouldBe(0, 75, [0, 128,0,255]);
  //right edge
  context.putImageData(buffer, 90, 0);
  pixelShouldBe(99, 25, [0, 128,0,255]);
  pixelShouldBe(99, 50, [0, 128,0,255]);
  pixelShouldBe(99, 75, [0, 128,0,255]);
  //top edge
  context.putImageData(buffer, 0, -90);
  pixelShouldBe(25, 0, [0, 128,0,255]);
  pixelShouldBe(50, 0, [0, 128,0,255]);
  pixelShouldBe(75, 0, [0, 128,0,255]);
  //bottom edge
  context.putImageData(buffer, 0, 90);
  pixelShouldBe(25, 99, [0, 128,0,255]);
  pixelShouldBe(50, 99, [0, 128,0,255]);
  pixelShouldBe(75, 99, [0, 128,0,255]);

  // Test drawing with only part of the dirty region intersecting the window
  // left edge
  context.putImageData(buffer, 0, 0, -90, 0, 100, 100);
  pixelShouldBe(0, 25, [0, 128,0,255]);
  pixelShouldBe(0, 50, [0, 128,0,255]);
  pixelShouldBe(0, 75, [0, 128,0,255]);
  pixelShouldBe(10, 25, [0, 128,0,255]);
  pixelShouldBe(10, 50, [0, 128,0,255]);
  pixelShouldBe(10, 75, [0, 128,0,255]);
  //right edge
  context.putImageData(buffer, 0, 0, 90, 0, 100, 100);
  pixelShouldBe(99, 25, [0, 128,0,255]);
  pixelShouldBe(99, 50, [0, 128,0,255]);
  pixelShouldBe(99, 75, [0, 128,0,255]);
  pixelShouldBe(89, 25, [0, 128,0,255]);
  pixelShouldBe(89, 50, [0, 128,0,255]);
  pixelShouldBe(89, 75, [0, 128,0,255]);
  // top edge
  context.putImageData(buffer, 0, 0, 0, -90, 100, 100);
  pixelShouldBe(25, 0, [0, 128,0,255]);
  pixelShouldBe(50, 0, [0, 128,0,255]);
  pixelShouldBe(75, 0, [0, 128,0,255]);
  pixelShouldBe(25, 10, [0, 128,0,255]);
  pixelShouldBe(50, 10, [0, 128,0,255]);
  pixelShouldBe(75, 10, [0, 128,0,255]);
  //bottom edge
  context.putImageData(buffer, 0, 0, 0, 90, 100, 100);
  pixelShouldBe(25, 99, [0, 128,0,255]);
  pixelShouldBe(50, 99, [0, 128,0,255]);
  pixelShouldBe(75, 99, [0, 128,0,255]);
  pixelShouldBe(25, 89, [0, 128,0,255]);
  pixelShouldBe(50, 89, [0, 128,0,255]);
  pixelShouldBe(75, 89, [0, 128,0,255]);

  // Test clamping of dx/dy
  var smallbuffer = context.createImageData(10, 10);
  fillRect(smallbuffer, 0, 0, 10, 10, 255, 0, 0, 255);
  context.putImageData(smallbuffer, 1.5, 1);
  pixelShouldBe(11, 11, [0, 128,0,255]);
  fillRect(smallbuffer, 0, 0, 10, 10, 0, 128, 0, 255);
  context.putImageData(smallbuffer, 1.5, 1);
  pixelShouldBe(1, 1, [0, 128,0,255]);

  // test clamping of dirtyX/Y/Width/Height
  fillRect(smallbuffer, 0, 0, 10, 10, 0, 128, 0, 255);
  context.fillStyle = "red";
  context.fillRect(1, 1, 9, 9);
  context.putImageData(smallbuffer, 1, 1, 0.5, 0.5, 8.5, 8.5);
  pixelShouldBe(1, 1, [0, 128,0,255]);
  pixelShouldBe(10, 10, [0, 128,0,255]);
  context.fillRect(1, 1, 9, 9);
  context.putImageData(smallbuffer, 1, 1, 0.25, 0.25, 9, 9);
  pixelShouldBe(1, 1, [0, 128,0,255]);
  pixelShouldBe(10, 10, [0, 128,0,255]);
  context.fillRect(1, 1, 8, 8);
  context.putImageData(smallbuffer, 1, 1, 0.0, 0.0, 8.5, 8.5);
  pixelShouldBe(1, 1, [0, 128,0,255]);
  pixelShouldBe(9, 9, [0, 128,0,255]);
  context.fillRect(1, 1, 8, 8);
  context.putImageData(smallbuffer, 1, 1, 0.0, 0.0, 8.25, 8.25);
  pixelShouldBe(1, 1, [0, 128,0,255]);
  pixelShouldBe(9, 9, [0, 128,0,255]);
  context.fillRect(1, 1, 7, 7);
  context.putImageData(smallbuffer, 1, 1, 0.5, 0.5, 7.9, 7.9);
  pixelShouldBe(1, 1, [0, 128,0,255]);
  pixelShouldBe(9, 9, [0, 128,0,255]);

  shouldThrow(() => context.putImageData({}, 0, 0));
  shouldThrow(() => context.putImageData(buffer, NaN, 0, 0, 0, 0, 0));
  shouldThrow(() => context.putImageData(buffer, 0, NaN, 0, 0, 0, 0));
  shouldThrow(() => context.putImageData(buffer, 0, 0, NaN, 0, 0, 0));
  shouldThrow(() => context.putImageData(buffer, 0, 0, 0, NaN, 0, 0));
  shouldThrow(() => context.putImageData(buffer, 0, 0, 0, 0, NaN, 0));
  shouldThrow(() => context.putImageData(buffer, 0, 0, 0, 0, 0, NaN));
  shouldThrow(() => context.putImageData(buffer, Inf, 0, 0, 0, 0, 0));
  shouldThrow(() => context.putImageData(buffer, 0, Inf, 0, 0, 0, 0));
  shouldThrow(() => context.putImageData(buffer, 0, 0, Inf, 0, 0, 0));
  shouldThrow(() => context.putImageData(buffer, 0, 0, 0, Inf, 0, 0));
  shouldThrow(() => context.putImageData(buffer, 0, 0, 0, 0, Inf, 0));
  shouldThrow(() => context.putImageData(buffer, 0, 0, 0, 0, 0, Inf));
  shouldThrow(() => context.putImageData(buffer, null, 0, 0, 0, 0, 0));
  shouldThrow(() => context.putImageData(buffer, 0, null, 0, 0, 0, 0));
  shouldThrow(() => context.putImageData(buffer, 0, 0, null, 0, 0, 0));
  shouldThrow(() => context.putImageData(buffer, 0, 0, 0, null, 0, 0));
  shouldThrow(() => context.putImageData(buffer, 0, 0, 0, 0, null, 0));
  shouldThrow(() => context.putImageData(buffer, 0, 0, 0, 0, 0, null));

  // Ensure we don't mess up bounds clipping checks
  var rectcanvas = document.createElement("canvas");
  rectcanvas.width = 20;
  rectcanvas.height = 10;
  var rectbuffer = rectcanvas.getContext("2d");
  rectbuffer.putImageData(smallbuffer, 10, 0);

  rectcanvas = document.createElement("canvas");
  rectcanvas.width = 10;
  rectcanvas.height = 20;
  rectbuffer = rectcanvas.getContext("2d");
  rectbuffer.putImageData(smallbuffer, 0, 10);
}
